//
// Created by 77873 on 2020/10/12 0012.
//

#include "VideoDecodec.h"

VideoDecodec::VideoDecodec(CallBackToJava *cbj, PlayStatus *ps) {
    this->callBackToJava = cbj;
    this->playStatus = ps;
    videoDataQueue = new DataQueue(playStatus);
    pthread_mutex_init(&codecMutex, NULL);
}

VideoDecodec::~VideoDecodec() {
    pthread_mutex_destroy(&codecMutex);
}

void *playVideo(void *data) {
    VideoDecodec *videoDecodec = static_cast<VideoDecodec *>(data);


    while (NULL != videoDecodec->playStatus && !videoDecodec->playStatus->isExit) {
//        LOGE("*playVideo(void *data)");
        if (videoDecodec->playStatus->isSeek) {
            LOGE("VideoDecodec::playVideo()  is seeking");
            av_usleep(1000 * 100);
            continue;
        }

        if (videoDecodec->playStatus->pause) {
            av_usleep(1000 * 100);
            continue;
        }

        if (videoDecodec->videoDataQueue->getQueueSIze() == 0) {
            if (!videoDecodec->playStatus->isLoad) {
                videoDecodec->playStatus->isLoad = true;
                videoDecodec->callBackToJava->onCallLoad(CHILD_THREAD, true);
            }
            av_usleep(1000 * 100);
            continue;
        } else {
            if (videoDecodec->playStatus->isLoad) {
                videoDecodec->playStatus->isLoad = false;
                videoDecodec->callBackToJava->onCallLoad(CHILD_THREAD, false);
            }
        }

        AVPacket *avPacket = av_packet_alloc();
        if (videoDecodec->videoDataQueue->popAvpacket(avPacket) != 0) {
            av_packet_free(&avPacket);
            av_free(avPacket);
            continue;
        }
        if (videoDecodec->codecType == CODEC_MEDIACODEC) {
//            LOGE("硬解码");

            if (av_bsf_send_packet(videoDecodec->avbsfContext, avPacket) != 0) {
                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;
                continue;
            }
            while (av_bsf_receive_packet(videoDecodec->avbsfContext, avPacket) == 0) {


                double  diff=videoDecodec->getFrameDiffTime(NULL,avPacket);
                av_usleep(videoDecodec->getDelayeTime(diff) * 1000 * 1000);

                videoDecodec->callBackToJava->decodecAvpacket(
                        avPacket->size, avPacket->data);


                av_packet_free(&avPacket);
                av_free(avPacket);
                continue;
            }
            avPacket = NULL;
        } else if (videoDecodec->codecType == CODEC_YUV) {


            pthread_mutex_lock(&videoDecodec->codecMutex);
            if (avcodec_send_packet(videoDecodec->videoCodecContex, avPacket)) {
                av_packet_free(&avPacket);
                av_free(avPacket);
                pthread_mutex_unlock(&videoDecodec->codecMutex);
                continue;
            }
            AVFrame *avFrame = av_frame_alloc();
            if (avcodec_receive_frame(videoDecodec->videoCodecContex, avFrame)) {
                av_frame_free(&avFrame);
                av_free(avFrame);
                avFrame = NULL;

                av_packet_free(&avPacket);
                av_free(avPacket);
                avPacket = NULL;

                pthread_mutex_unlock(&videoDecodec->codecMutex);
                continue;
            }



            /**
             * 如果是yuv420P格式的数据
             */
            if (avFrame->format == AV_PIX_FMT_YUV420P) {
                LOGE("yuv 420 格式数据");

                double diff = videoDecodec->getFrameDiffTime(NULL, avPacket);
                LOGE("diff is %f", diff);

                av_usleep(videoDecodec->getDelayeTime(diff) * 1000000);

                if (NULL != videoDecodec && NULL != videoDecodec->videoCodecContex) {
                    videoDecodec->callBackToJava->onCallRenderYuv(
                            videoDecodec->videoCodecContex->width,
                            videoDecodec->videoCodecContex->height,
                            avFrame->data[0],
                            avFrame->data[1],
                            avFrame->data[2]
                    );
                }
            } else {
                LOGE("非 YUV420p格式数据");

                AVFrame *avFrameYuv420P = av_frame_alloc();
                int num = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,
                                                   videoDecodec->videoCodecContex->width,
                                                   videoDecodec->videoCodecContex->height,
                                                   1);
                uint8_t *buffer = static_cast<uint8_t *>(av_malloc(num * sizeof(uint8_t)));

                av_image_fill_arrays(avFrameYuv420P->data,
                                     avFrameYuv420P->linesize,
                                     buffer,
                                     AV_PIX_FMT_YUV420P,
                                     videoDecodec->videoCodecContex->width,
                                     videoDecodec->videoCodecContex->height, 1);

                SwsContext *swr_ctx = sws_getContext(
                        videoDecodec->videoCodecContex->width,
                        videoDecodec->videoCodecContex->height,
                        videoDecodec->videoCodecContex->pix_fmt,
                        videoDecodec->videoCodecContex->width,
                        videoDecodec->videoCodecContex->height,
                        AV_PIX_FMT_YUV420P,
                        SWS_BICUBIC, NULL, NULL, NULL
                );
                if (!swr_ctx) {
                    av_frame_free(&avFrameYuv420P);
                    av_free(avFrameYuv420P);
                    av_free(buffer);
                    pthread_mutex_unlock(&videoDecodec->codecMutex);
                    continue;
                }
                sws_scale(swr_ctx,
                          avFrame->data,
                          avFrame->linesize,
                          0,
                          avFrame->height,
                          avFrameYuv420P->data,
                          avFrameYuv420P->linesize);

                double diff = videoDecodec->getFrameDiffTime(avFrame, NULL);
                av_usleep(videoDecodec->getDelayeTime(diff) * 1000 * 1000);

                //回调
                videoDecodec->callBackToJava->onCallRenderYuv(
                        videoDecodec->videoCodecContex->width,
                        videoDecodec->videoCodecContex->height,
                        avFrameYuv420P->data[0],
                        avFrameYuv420P->data[1],
                        avFrameYuv420P->data[2]
                );

                av_frame_free(&avFrameYuv420P);
                av_free(avFrameYuv420P);
                av_free(buffer);
                sws_freeContext(swr_ctx);
            }

            av_frame_free(&avFrame);
            av_free(avFrame);
            avFrame = NULL;



//        LOGE("pthread_mutex_unlock(&videoDecodec->codecMutex);");
            pthread_mutex_unlock(&videoDecodec->codecMutex);
        }
        av_packet_free(&avPacket);
        av_free(avPacket);
        avPacket = NULL;

    }
//    pthread_exit(&videoDecodec->thread_play);
    return 0;
}

void VideoDecodec::startPlay() {
    if(NULL!=playStatus&&!playStatus->isExit){
        pthread_create(&thread_play, NULL, playVideo, this);
    }

}

void VideoDecodec::release() {

    if(NULL!=videoDataQueue){
        videoDataQueue->noticeQueue();
    }

    pthread_join(thread_play,NULL);

    if (NULL != videoDataQueue) {
        delete (videoDataQueue);
        videoDataQueue = NULL;
    }

    if (NULL != avbsfContext) {
        av_bsf_free(&avbsfContext);
        av_free(avbsfContext);
        avbsfContext = NULL;

    }
    if (NULL != videoCodecContex) {
        avcodec_close(videoCodecContex);
        avcodec_free_context(&videoCodecContex);
        videoCodecContex = NULL;
    }
    if (NULL != playStatus) {
        playStatus = NULL;
    }

}

double VideoDecodec::getFrameDiffTime(AVFrame *avFrame, AVPacket *avPacket) {

    double pts = 0;
    if (avFrame != NULL) {
        pts = av_frame_get_best_effort_timestamp(avFrame);
    }
    if (avPacket != NULL) {
        pts = avPacket->pts;
    }
    if (pts == AV_NOPTS_VALUE) {
        pts = 0;
    }
    pts *= av_q2d(videoBaseTime);

    if (pts > 0) {
        clock = pts;
    }

    double diff = audioDecodec->clock - clock;
    return diff;
}

double VideoDecodec::getDelayeTime(double diff) {
    if (diff > 0) {
        LOGE("音频快了  %f",diff);
    } else if(diff==0){

    } else{
        LOGE("视频快了  %f",diff);
    }


    if (diff > 0.003) {
        delayTime = delayTime * 2 / 3;
        if (delayTime < defaultDelayTime / 2) {
            delayTime = defaultDelayTime * 2 / 3;
        } else if (delayTime > defaultDelayTime * 2) {
            delayTime = defaultDelayTime * 2;
        }
    } else if (diff < -0.003) {
        delayTime = delayTime * 3 / 2;
        if (delayTime < defaultDelayTime / 2) {
            delayTime = defaultDelayTime * 2 / 3;
        } else if (delayTime > defaultDelayTime * 2) {
            delayTime = defaultDelayTime * 2;
        }
    } else if (diff == 0.003) {

    }
    if (diff >= 0.5) {
        delayTime = 0;
    } else if (diff <= -0.5) {
        delayTime = defaultDelayTime * 2;
    }

    if (fabs(diff) >= 10) {
        delayTime = defaultDelayTime;
    }
    return delayTime;
}

